﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net.Sockets;
using System.Text;
using System.Threading.Tasks;

namespace ImageUploader
{
    /// <summary>
    /// Net：提供静态方法，对常用的网络操作进行封装
    /// </summary>
    public sealed class Net
    {
        private Net()
        {
        }

        /// <summary>
        /// 向主机发送数据
        /// </summary>
        /// <param name="socket">要发送数据且已经连接到远程主机的Socket</param>
        /// <param name="buffer">待发送的数据</param>
        /// <param name="outTime">发送数据的超时时间，以秒为单位，可精确到毫秒</param>
        /// <returns>0:发送成功；-1：发送超时；-2：发送数据出错；-3：发送数据异常</returns>
        /// <remarks>当outTime指定为-1时，将一直等待直到有数据发送</remarks>
        public static int SendData(Socket socket, byte[] buffer, int outTime)
        {
            if(socket == null || socket.Connected == false)
            {
                throw new ArgumentException("参数socket 为null，或者未连接到远程计算机");
            }
            if(buffer == null || buffer.Length == 0)
            {
                throw new ArgumentException("参数buffer为null，或长度为0");
            }
            int flag = 0;
            try
            {
                int left = buffer.Length;
                int sndLen = 0;
                while(true)
                {
                    if ((socket.Poll(outTime * 100, SelectMode.SelectWrite) == true))
                    {
                        sndLen = socket.Send(buffer, sndLen, left, SocketFlags.None);
                        left -= sndLen;
                        if(left == 0)
                        {
                            flag = 0;//数据已经全部发送
                            break;
                        }
                        else
                        {
                            if(sndLen > 0)
                            {
                                continue;//部分数据已被发送
                            }
                            else
                            {
                                flag = -2;
                                break;
                            }
                        }
                    }
                    else
                    {
                        flag = -1;//超时退出；
                        break;
                    }
                }
            }
            catch(SocketException e)
            {
                flag = -3;
            }
            return flag;
        }

        /// <summary>
        /// 向远程主机发送文件
        /// </summary>
        /// <param name="socket" >要发送数据且已经连接到远程主机的 socket</param>
        /// <param name="fileName">待发送的文件名称</param>
        /// <param name="maxBufferLength">文件发送时的缓冲区大小</param>
        /// <param name="outTime">发送缓冲区中的数据的超时时间</param>
        /// <returns>0:发送文件成功；-1:超时；-2:发送文件出现错误；-3:发送文件出现异常；-4:读取待发送文件发生错误</returns>
        /// <remarks >
        /// 当 outTime 指定为-1时，将一直等待直到有数据需要发送
        /// </remarks>
        public static int SendFile(Socket socket, string fileName, int maxBufferLength, int outTime)
        {
            if (fileName == null || maxBufferLength <= 0)
            {
                throw new ArgumentException("待发送的文件名称为空或发送缓冲区的大小设置不正确");
            }
            int flag = 0;
            try
            {
                FileStream fs = new FileStream(fileName, FileMode.Open, FileAccess.Read);
                long fileLen = fs.Length;   //文件长度
                long leftLen = fileLen;     //未读部分
                int readLen = 0;            //已读部分
                byte[] buffer = null;

                if(fileLen <= maxBufferLength)
                {
                    //文件可以被一次读取
                    buffer = new byte[fileLen];
                    readLen = fs.Read(buffer, 0, (int)fileLen);
                    flag = SendData(socket, buffer, outTime);
                }
                else
                {
                    //文件不能被一次读取，循环读取文件，并发送
                    while (leftLen != 0)
                    {
                        if(leftLen < maxBufferLength)
                        {
                            buffer = new byte[leftLen];
                            readLen = fs.Read(buffer, 0, Convert.ToInt32(leftLen));
                        }
                        else
                        {
                            buffer = new byte[maxBufferLength];
                            readLen = fs.Read(buffer, 0, maxBufferLength);
                        }
                        if ((flag = SendData(socket, buffer, outTime)) < 0)
                        {
                            break;
                        }
                        leftLen -= readLen;
                    }
                }
                fs.Flush();
                fs.Close();
            }
            catch(IOException e)
            {
                flag = -4;
            }
            return flag;
        }
    }
}
